/*
 * RMCommitmentAction.java   0.0.1    9 Nov 2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transactions.control.impl;

import org.mentalsmash.tazio.transactions.control.CommitmentAction;
import org.mentalsmash.tazio.transactions.control.TransactionStateException;
import org.mentalsmash.tazio.transactions.control.ValidationAgent;
import org.mentalsmash.tazio.transactions.model.RootSet;
import org.mentalsmash.tazio.transactions.user.RollbackException;
import org.mentalsmash.tazio.transactions.user.TransactionStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Andrea Reale
 * @version 0.0.1
 */
public class RMCommitmentAction extends CommitmentAction {

    final static Logger log = LoggerFactory.getLogger(RMCommitmentAction.class);

    private final RMTransactionManager tm;

    private final ValidationAgent validationAgent;

    public RMCommitmentAction(final TazioTransactionImpl toCommit,
	    final RMTransactionManager tm, final RootSet rs) {
	super(toCommit, rs);
	this.tm = tm;
	this.validationAgent = tm.getValidationAgent();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.concurrent.Callable#call()
     */
    @Override
    public TransactionStatus call() throws Exception {

	// locking scheme
	this.tm.getCommitmentLock().lock();
	try {
	    while (!this.tm.isCommitmentSectionFree()) {
		this.tm.getCommitmentSectionCondition().awaitUninterruptibly();
	    }
	    this.tm.setCommitmentSectionFree(false);

	    doValidateAndCommmit();

	    this.tm.setCommitmentSectionFree(true);
	    this.tm.getCommitmentSectionCondition().signal();
	} finally {
	    this.tm.getCommitmentLock().unlock();
	}

	return this.toCommit.getStatus();

    }

    /**
     * @throws TransactionStateException
     * @throws RollbackException
     */
    private void doValidateAndCommmit() throws TransactionStateException,
	    RollbackException {
	if (this.toCommit.getStatus() != TransactionStatus.GLOBAL_VALIDATION) {
	    log
		    .error("Transaction"
			    + this.toCommit
			    + " that wasn't in the GLOBAL_VALIDATIONS status was asked to commit");
	    throw new TransactionStateException("Transaction " + this.toCommit
		    + " was not in the GLOBAL_VALIDATION status");
	}

	if (this.toCommit.isRollbackOnly()) {
	    this.toCommit.stateTransition(TransactionStatus.ROLLED_BACK);
	    log.debug("Transaction {} rolled back", this.toCommit);
	    throw new RollbackException("Transaction " + this.toCommit
		    + " was set to rollback only");
	}

	log.trace("{} started its GLOBAL_VALIDATION", this.toCommit);

	if (!this.validationAgent.validate(this.toCommit)) {

	    this.toCommit.stateTransition(TransactionStatus.ROLLED_BACK);
	    log.debug("Transaction {} rolled back", this.toCommit);
	    throw new RollbackException(
		    "Global validation failed. Transaction " + this.toCommit
			    + " rolled back");
	}

	log.trace("{} passed the global validation", this.toCommit);

	log.trace("{} is applying its changes to the root set", this.toCommit);
	this.toCommit.setSeqNo(this.tm.incrementAndGetSeqNoCtr());

	doApplyChanges();
	this.toCommit.stateTransition(TransactionStatus.COMMITTED);

	log.trace("{} commited", this.toCommit);

    }

}
